Generic rive
The generic Rive animation system allows you to create complex, interactive onboarding experiences entirely through JSON configuration using JEXL expression.
Your Rive animation must have a View Model with:
- Input properties (triggers, booleans, numbers, strings)
- Output properties (strings, images for dynamic content)
- State machine with named states
How to Check Your Rive File
- Open your
.rivfile in Rive Editor - Go to the View Model panel
- Verify all property names match your JSON config exactly (case-sensitive)
- Note the State Machine name (usually
state_machine_1)
View Model Properties :
Property Types
All properties used in your animation must be declared in viewModelProperties:
STRING
"resultTitle": {
"type": "STRING",
"value": "{{data.cards[randomIndex].title}}"
}
IMAGE
"profileImage": {
"type": "IMAGE",
"value": "{{data.cards[randomIndex].image}}"
}
- Supports absolute URLs:
<https://example.com/image.png> - Supports relative paths:
/assets/image.png
BOOLEAN
"isVisible": {
"type": "BOOLEAN",
"value": "{{answers.showProfile == 'yes'}}"
}
- String values
"true"and"false"are parsed correctly
NUMBER
"score": {
"type": "NUMBER",
"value": "{{answers.totalPoints}}"
}
Initial Values vs State-Based Updates
Initial Values (set on load)
"viewModelProperties": {
"userName": {
"type": "STRING",
"value": "{{answers.name}}"
}
}
State-Based Updates (set when state changes)
"stateFields": [
{
"stateName": "result screen",
"viewModelProperties": [
{
"key": "resultTitle",
"value": "{{data.results[selectedIndex].title}}"
}
]
}
]
State Management
Defining States
List all states you want to handle:
"states": ["intro", "question_1", "question_2", "result screen"]
State-Based Property Updates
When a state is entered, update properties:
"stateFields": [
{
"stateName": "question_1",
"viewModelProperties": [
{
"key": "questionText",
"value": "{{data.questions[0].text}}"
},
{
"key": "questionImage",
"value": "{{data.questions[0].image}}"
}
]
}
]
Triggers
Triggers are fired when users interact with your Rive animation.
Basic Trigger
"triggers": {
"button clicked": {
"execute": "{{moveToNextStep()}}"
}
}
Multi-Statement Triggers
Separate statements with semicolons:
"triggers": {
"card selected": {
"execute": "{{createAndRandomizeNumberProperty('cardIndex', 3); setRiveViewModelNumber('cardNumber', 1); setRiveViewModelBoolean('showResult', true)}}"
}
}
Trigger Best Practices
- Name triggers exactly as they appear in Rive (case-sensitive)
- Keep logic simple - use memory properties for complex state
- Test each trigger individually before combining
Data & Dynamic Content
Data Structure
Store your content in the data field:
"data": {
"cards": [
{
"name": "The Moon",
"result": {
"title": "Moon Howler",
"image": "https://example.com/moon.png",
"description": "Your profile..."
},
"questions": [
{
"text": "Question 1?",
"image": "/assets/q1.png"
}
]
}
]
}
Accessing Data with Memory Properties
- Create a memory property (in a trigger):
"execute": "{{createAndRandomizeNumberProperty('selectedCard', 3)}}"
- Reference it in data paths:
"value": "{{data.cards[selectedCard].result.title}}"
Dynamic Array Joining
"resultBulletPoints": {
"type": "STRING",
"value": "{{joinWithNewLines(data.cards[selectedCard].bulletPoints)}}"
}
| Key | Notes |
|---|---|
type | interactiveAnimation |
renderer | webgl |
autoBind | true |
autoplay | true |
states | an array of valid states names for state change events. for example in tarot case states:["past_main","result screen"] |
stateFields | Note : Each state in the animation has its own stateFields config with specific viewModelProperties . When the Rive animation transitions to a state, it applies the corresponding view model properties defined for that state. "stateFields": [ { "stateName": "result screen", "viewModelProperties": [ { "key": "resultTitle", "value": "{{data.results[selectedIndex].title}}" } ] } ] |
viewModelPropirties | "viewModelProperties": { "resultTitle": { "type": "STRING" }, "profileImage": { "type": "IMAGE" } }, **Note :**All properties used in your animation must be declared in viewModelProperties |
| triggers | Triggers are fired when users interact with your Rive animation. Basic Trigger "triggers": { "button clicked": { "execute": "{{moveToNextStep()}}" } } Multi-Statement Triggers Separate statements with semicolons: "triggers": { "card selected": { "execute": "{{createAndRandomizeNumberProperty('cardIndex', 3); setRiveViewModelNumber('cardNumber', 1); setRiveViewModelBoolean('showResult', true)}}" } } |
data | Store your content in the data field: "data": { "cards": [ { "name": "The Moon", "result": { "fallbackLimit": 4, "profileTitle": "Anxiety Profile", "profileName": "Moon Howler", "cardDrawn": "Card drawn: The Moon", "paragraph": "Your anxiety has deep, hidden roots, like the wolf’s howl echoing in the night. It makes you prone to:", "image": "https://c.thefab.co/web-onboarding/mind/profile_image.webp", "fallbackBulletPoints": [ "Escapism", "Masking", "Attachment Wounds", "Cognitive Distortions" ], "bottomText": "But that’s your past... Aren’t you curious about your future?" }, "questionsData": [ { "text": "The Moon is a mysterious and emotional card.", "image": "https://c.thefab.co/web-onboarding/mind/moon-illustration.webp" }, { "text": "Which of these feels most connected to your past?", "image": "https://c.thefab.co/web-onboarding/mind/moon-illustration.webp" }, { "subject": "Escapism", "text": "Do you retreat into fantasy or distractions when life feels heavy?", "image": "https://c.thefab.co/web-onboarding/mind/moon-frame1.webp" }, { "subject": "Masking", "text": "Do you hide your true feelings behind a smile?", "image": "https://c.thefab.co/web-onboarding/mind/moon-frame2.webp" }, { "subject": "Cognitive Distortions", "text": "Do your thoughts often expect the worst or doubt yourself?", "image": "https://c.thefab.co/web-onboarding/mind/moon-frame3.webp" }, { "subject": "Attachment Wounds", "text": "Do past relationship hurts still affect your trust?", "image": "https://c.thefab.co/web-onboarding/mind/moon-frame4.webp" } ] }, // more cards Note : For the result it has fallbackLimit Logic: If user provides fewer than this number of resonating answers, show fallbackBulletPoints Example: 4 means if user resonates with fewer than 4 questions, show generic bullet points |
Full json example
{
"id": "fab_tarot",
"appId": "co.thefabulous.app",
"language": "en",
"seo": {
"title": "Fabulous",
"favicon": "https://c.thefab.co/web-onboarding/elixir/elixir-favicon.png",
"description": "Unlock your true potential with Elixir: Your daily guide to a purpose-driven, authentic life. Find your focus, live with intention."
},
"steps": [
{
"type": "interactiveAnimation",
"stepId": "tarot_animation",
"backgroundColor": "#1A235E",
"textColor": "#FFFFFF",
"animation": {
"renderer": "webgl",
"url": "/assets/rive/tarot_onboarding_v40.1.14.riv",
"stateMachine": "state_machine_1",
"autoplay": true,
"autoBind": true,
"states": [
"past_main",
"result screen"
],
"stateFields": [
{
"stateName": "past_main",
"viewModelProperties": [
{
"key": "introTitle",
"value": "Discover who you are and what drives your Anxiety"
},
{
"key": "title2",
"value": "Think about your childhood, Stranger. Then Pick a card."
}
]
},
{
"stateName": "result screen",
"viewModelProperties": [
{
"key": "resultProfileTitle",
"value": "{{data.cards[randomCardIndex].result.profileTitle}}"
},
{
"key": "resultProfileName",
"value": "{{data.cards[randomCardIndex].result.profileName}}"
},
{
"key": "resultCardDrawn",
"value": "{{data.cards[randomCardIndex].result.cardDrawn}}"
},
{
"key": "resultParagraph",
"value": "{{data.cards[randomCardIndex].result.paragraph}}"
},
{
"key": "profileImage",
"value": "{{data.cards[randomCardIndex].result.image}}"
},
{
"key": "resultBulletPoints",
"value": "{{joinWithNewLines(data.cards[randomCardIndex].result.fallbackBulletPoints)}}"
},
{
"key": "resultBottomText",
"value": "{{data.cards[randomCardIndex].result.bottomText}}"
}
]
}
],
"viewModelProperties": {
"introTitle": {
"type": "STRING"
},
"title2": {
"type": "STRING"
},
"title3": {
"type": "STRING",
"value": "{{data.cards[randomCardIndex].questionsData[0].text}}"
},
"title4": {
"type": "STRING",
"value": "{{data.cards[randomCardIndex].questionsData[1].text}}"
},
"introImage": {
"type": "IMAGE",
"value": "{{data.cards[randomCardIndex].questionsData[0].image}}"
},
"question1": {
"type": "STRING",
"value": "{{data.cards[randomCardIndex].questionsData[2].text}}"
},
"questionImage1": {
"type": "IMAGE",
"value": "{{data.cards[randomCardIndex].questionsData[2].image}}"
},
"question2": {
"type": "STRING",
"value": "{{data.cards[randomCardIndex].questionsData[3].text}}"
},
"questionImage2": {
"type": "IMAGE",
"value": "{{data.cards[randomCardIndex].questionsData[3].image}}"
},
"question3": {
"type": "STRING",
"value": "{{data.cards[randomCardIndex].questionsData[4].text}}"
},
"questionImage3": {
"type": "IMAGE",
"value": "{{data.cards[randomCardIndex].questionsData[4].image}}"
},
"question4": {
"type": "STRING",
"value": "{{data.cards[randomCardIndex].questionsData[5].text}}"
},
"questionImage4": {
"type": "IMAGE",
"value": "{{data.cards[randomCardIndex].questionsData[5].image}}"
},
"question5": {
"type": "STRING",
"value": "{{data.cards[randomCardIndex].questionsData[2].text}}"
},
"questionImage5": {
"type": "IMAGE",
"value": "{{data.cards[randomCardIndex].questionsData[2].image}}"
},
"question6": {
"type": "STRING",
"value": "{{data.cards[randomCardIndex].questionsData[3].text}}"
},
"questionImage6": {
"type": "IMAGE",
"value": "{{data.cards[randomCardIndex].questionsData[3].image}}"
},
"question7": {
"type": "STRING",
"value": "{{data.cards[randomCardIndex].questionsData[4].text}}"
},
"questionImage7": {
"type": "IMAGE",
"value": "{{data.cards[randomCardIndex].questionsData[4].image}}"
},
"question8": {
"type": "STRING",
"value": "{{data.cards[randomCardIndex].questionsData[5].text}}"
},
"questionImage8": {
"type": "IMAGE",
"value": "{{data.cards[randomCardIndex].questionsData[5].image}}"
},
"question9": {
"type": "STRING",
"value": "{{data.cards[randomCardIndex].questionsData[2].text}}"
},
"questionImage9": {
"type": "IMAGE",
"value": "{{data.cards[randomCardIndex].questionsData[2].image}}"
},
"question10": {
"type": "STRING",
"value": "{{data.cards[randomCardIndex].questionsData[3].text}}"
},
"questionImage10": {
"type": "IMAGE",
"value": "{{data.cards[randomCardIndex].questionsData[3].image}}"
},
"cardName": {
"type": "STRING",
"value": "{{data.cards[randomCardIndex].name}}"
},
"resultProfileTitle": {
"type": "STRING"
},
"resultProfileName": {
"type": "STRING"
},
"resultCardDrawn": {
"type": "STRING"
},
"resultParagraph": {
"type": "STRING"
},
"resultBulletPoints": {
"type": "STRING"
},
"resultBottomText": {
"type": "STRING"
},
"profileImage": {
"type": "IMAGE"
}
},
"triggers": {
"card selected": {
"execute": "{{createAndRandomizeNumberProperty('randomCardIndex', 3); setRiveViewModelNumber('questions number', 4); setRiveViewModelBoolean('future or past', true); createNumberProperty('currentQuestion', 0)}}"
},
"explain": {
"execute": "{{incrementProperty('currentQuestion');}}"
},
"i will": {
"execute": "{{incrementProperty('currentQuestion');}}"
},
"not sure": {
"execute": "{{incrementProperty('currentQuestion');}}"
},
"this resonate": {
"execute": "{{incrementProperty('currentQuestion');}}"
},
"draw future card": {
"execute": "{{moveToNextStep()}}"
}
},
"data": {
"cards": [
{
"name": "The Moon",
"result": {
"fallbackLimit": 4,
"profileTitle": "Anxiety Profile",
"profileName": "Moon Howler",
"cardDrawn": "Card drawn: The Moon",
"paragraph": "Your anxiety has deep, hidden roots, like the wolf’s howl echoing in the night. It makes you prone to:",
"image": "https://c.thefab.co/web-onboarding/mind/profile_image.webp",
"fallbackBulletPoints": [
"Escapism",
"Masking",
"Attachment Wounds",
"Cognitive Distortions"
],
"bottomText": "But that’s your past... Aren’t you curious about your future?"
},
"questionsData": [
{
"text": "The Moon is a mysterious and emotional card.",
"image": "https://c.thefab.co/web-onboarding/mind/moon-illustration.webp"
},
{
"text": "Which of these feels most connected to your past?",
"image": "https://c.thefab.co/web-onboarding/mind/moon-illustration.webp"
},
{
"subject": "Escapism",
"text": "Do you retreat into fantasy or distractions when life feels heavy?",
"image": "https://c.thefab.co/web-onboarding/mind/moon-frame1.webp"
},
{
"subject": "Masking",
"text": "Do you hide your true feelings behind a smile?",
"image": "https://c.thefab.co/web-onboarding/mind/moon-frame2.webp"
},
{
"subject": "Cognitive Distortions",
"text": "Do your thoughts often expect the worst or doubt yourself?",
"image": "https://c.thefab.co/web-onboarding/mind/moon-frame3.webp"
},
{
"subject": "Attachment Wounds",
"text": "Do past relationship hurts still affect your trust?",
"image": "https://c.thefab.co/web-onboarding/mind/moon-frame4.webp"
}
]
},
{
"name": "The Tower",
"result": {
"fallbackLimit": 4,
"profileTitle": "Anxiety Profile",
"profileName": "Tower Falcon",
"cardDrawn": "Card drawn: The Tower",
"paragraph": "Your anxiety is fueled by sudden disruptions, like a falcon trying to nest in a crumbling tower. It makes you prone to:",
"image": "https://c.thefab.co/web-onboarding/mind/falcon_profile.webp",
"fallbackBulletPoints": [
"Hypervigilance",
"Fear of Change",
"Emotional Flashbacks",
"Safety-Seeking Behavior"
],
"bottomText": "But that’s your past... Aren’t you curious about your future?"
},
"questionsData": [
{
"text": "The Tower shows upheaval and transformation.",
"image": "https://c.thefab.co/web-onboarding/mind/tower_illustration.webp"
},
{
"text": "Which of these resonates with your past?",
"image": "https://c.thefab.co/web-onboarding/mind/tower_illustration.webp"
},
{
"subject": "Hypervigilance",
"text": "Do you feel on edge, waiting for something to go wrong?",
"image": "https://c.thefab.co/web-onboarding/mind/tower_frame1.webp"
},
{
"subject": "Fear of Change",
"text": "Does even good change make you feel uneasy?",
"image": "https://c.thefab.co/web-onboarding/mind/tower_frame2.webp"
},
{
"subject": "Emotional Flashbacks",
"text": "Do past hurts come back as if they are happening now?",
"image": "https://c.thefab.co/web-onboarding/mind/tower_frame3.webp"
},
{
"subject": "Safety-Seeking Behavior",
"text": "Do you avoid risks because stability feels safer?",
"image": "https://c.thefab.co/web-onboarding/mind/tower_frame4.webp"
}
]
},
{
"name": "Justice",
"result": {
"fallbackLimit": 4,
"profileTitle": "Anxiety Profile",
"profileName": "Inner Judge",
"cardDrawn": "Card drawn: Justice",
"paragraph": "Your anxiety is shaped by self-criticism and inner judgment, like a strict judge weighing every action. It makes you prone to:",
"image": "https://c.thefab.co/web-onboarding/mind/balance_profile.webp",
"fallbackBulletPoints": [
"Self-Criticism",
"Perfectionism",
"Fear of Unfairness",
"Difficulty Letting Go"
],
"bottomText": "But that’s your past... Aren’t you curious about your future?"
},
"questionsData": [
{
"text": "Justice is a card of truth and accountability.",
"image": "https://c.thefab.co/web-onboarding/mind/justice_illustration.webp"
},
{
"text": "Which of these feels familiar from your past?",
"image": "https://c.thefab.co/web-onboarding/mind/justice_illustration.webp"
},
{
"subject": "Self-Criticism",
"text": "Do you judge yourself more harshly than others do?",
"image": "https://c.thefab.co/web-onboarding/mind/justice_frame1.webp"
},
{
"subject": "Perfectionism",
"text": "Do you feel you must get everything exactly right?",
"image": "https://c.thefab.co/web-onboarding/mind/justice_frame2.webp"
},
{
"subject": "Fear of Unfairness",
"text": "Does unfair treatment weigh heavily on your mind?",
"image": "https://c.thefab.co/web-onboarding/mind/justice_frame3.webp"
},
{
"subject": "Difficulty Letting Go",
"text": "Do you struggle to release grudges or past wrongs?",
"image": "https://c.thefab.co/web-onboarding/mind/justice_frame4.webp"
}
]
}
]
}
}
}
],
"logic": []
}